from django.views.decorators.csrf import csrf_exempt

from django.http import HttpResponse


# 卡管理相关视图：包含卡列表、添加、编辑等
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.shortcuts import render, redirect, get_object_or_404
from LogisticsCard.permission_dict import operator_cn, card_status_cn, card_type_cn, real_name_auth_cn
from LogisticsCard.models import Cards, SystemConfig, Log, CardUserRelation, UserProfile
from LogisticsCard.apis.iot_card_manage_api import multiple_cards_query, card_batch_info_query, single_card_detail_query
from django.contrib.auth.models import User
# 批量更新卡信息视图
@login_required
@csrf_exempt
def cards_update_view(request):
    """
    调用批量获取卡信息接口，返回卡信息，查表存在则更新，不存在则添加。
    """
    from django.contrib import messages
    # 获取API账号密码
    userName = SystemConfig.objects.get(key='api_user').value
    passWord = SystemConfig.objects.get(key='api_pass').value
    userName = userName if userName else ''
    passWord = passWord if passWord else ''
    if not userName or not passWord:
        messages.error(request, '未配置API账号密码')
        return redirect('/cards/')
    # 调用批量接口
    page_num = 1
    page_size = 100
    total_synced = 0
    while True:
        api_result = card_batch_info_query(userName, passWord, pageSize=page_size, pageNum=page_num)
        # 1. 必须是dict
        if not isinstance(api_result, dict):
            messages.error(request, f'接口返回异常：{api_result}')
            return redirect('/cards/')
        # 2. status判断
        if api_result.get('status') != 0:
            messages.error(request, f"接口错误：{api_result.get('message', '未知错误')}")
            return redirect('/cards/')
        # 3. data结构判断
        data = api_result.get('data')
        if not data or not isinstance(data, dict):
            messages.error(request, f'接口返回异常：{api_result}')
            return redirect('/cards/')
        # 4. records为卡信息列表
        card_data_list = data.get('records', [])
        if not isinstance(card_data_list, list):
            messages.error(request, f'接口返回数据格式错误：{api_result}')
            return redirect('/cards/')
        for card_data in card_data_list:
            iccid = card_data.get('iccid')
            if not iccid:
                continue
            from datetime import datetime, date
            def parse_date_safe(val):
                if not val or val in ('', 'None', None):
                    return None
                if isinstance(val, date) and not isinstance(val, datetime):
                    return val
                if isinstance(val, datetime):
                    return val.date()
                try:
                    return datetime.strptime(val[:10], '%Y-%m-%d').date()
                except Exception:
                    return None
            def parse_datetime_safe(val):
                if not val or val in ('', 'None', None):
                    return None
                if isinstance(val, datetime):
                    return val
                if isinstance(val, date):
                    return datetime.combine(val, datetime.min.time())
                try:
                    # 支持 "YYYY-MM-DD HH:MM:SS" 或 "YYYY-MM-DDTHH:MM:SS"
                    v = val.replace('T', ' ')
                    return datetime.strptime(v[:19], '%Y-%m-%d %H:%M:%S')
                except Exception:
                    try:
                        return datetime.strptime(val[:10], '%Y-%m-%d')
                    except Exception:
                        return None
            update_fields = {
                'msisdn': card_data.get('msisdn'),
                'imsi': card_data.get('imsi'),
                'imei': card_data.get('imei'),
                'cardType': card_data.get('cardType'),
                'cardStatus': card_data.get('cardStatus'),
                'operator': card_data.get('operator'),
                'packageName': card_data.get('packageName'),
                'totalFlow': card_data.get('totalFlow'),
                'cardFlow': card_data.get('cardFlow'),
                'activateTime': parse_date_safe(card_data.get('activateTime')),
                'packageTime': parse_date_safe(card_data.get('packageTime')),
                'remark': card_data.get('remark'),
                'realNameStatus': card_data.get('realNameStatus'),
                'channelId': card_data.get('channelId'),
                'packageId': card_data.get('packageId'),
                'priceTime': card_data.get('priceTime'),
                'poolId': card_data.get('poolId'),
                'networkType': card_data.get('networkType'),
                'packageTotalFlow': card_data.get('packageTotalFlow'),
                'isRealName': card_data.get('isRealName'),
                'offTime': parse_date_safe(card_data.get('offTime')),
                'dismantleTime': parse_datetime_safe(card_data.get('dismantleTime')),
                'orderId': card_data.get('orderId'),
                'buyTime': parse_datetime_safe(card_data.get('buyTime')),
                'leftFlow': card_data.get('leftFlow'),
                'openCardTime': parse_datetime_safe(card_data.get('openCardTime')),
                'lastActivateTime': parse_date_safe(card_data.get('lastActivateTime')),
            }
            updated = Cards.objects.filter(iccid=iccid, delete_at__isnull=True).update(**update_fields)
            if updated == 0:
                Cards.objects.create(iccid=iccid, **update_fields)
            total_synced += 1
        # 分页判断
        if data.get('pageNum') >= data.get('pageCount'):
            break
        page_num += 1
    messages.success(request, f'卡信息已批量同步（共{total_synced}条）')
    return redirect('/cards/')
@login_required
def import_cards_view(request):
    """
    卡导入视图，仅支持导入ICCID。上传txt文件，每行一个ICCID。
    """
    from django.contrib import messages
    import io
    import os
    iccid_list = []
    if request.method == 'POST' and request.FILES.get('file'):
        file = request.FILES['file']
        filename = file.name.lower()
        # txt文件处理
        if filename.endswith('.txt'):
            try:
                content = file.read().decode('utf-8')
            except Exception:
                messages.error(request, '文件解码失败，请上传UTF-8编码的txt文件')
                return redirect('/cards/import/')
            iccid_list = [line.strip() for line in content.splitlines() if line.strip()]
        # xlsx文件处理
        elif filename.endswith('.xlsx'):
            try:
                from openpyxl import load_workbook
                import tempfile
                with tempfile.NamedTemporaryFile(delete=False, suffix='.xlsx') as tmp:
                    tmp.write(file.read())
                    tmp_path = tmp.name
                wb = load_workbook(tmp_path, read_only=True)
                ws = wb.active
                for row in ws.iter_rows(min_row=1, max_col=1):
                    cell = row[0].value
                    if cell:
                        iccid_list.append(str(cell).strip())
                wb.close()
                os.remove(tmp_path)
            except Exception as e:
                messages.error(request, f'Excel文件读取失败: {e}')
                return redirect('/cards/import/')
        # xls文件处理
        elif filename.endswith('.xls'):
            try:
                import xlrd
                import tempfile
                with tempfile.NamedTemporaryFile(delete=False, suffix='.xls') as tmp:
                    tmp.write(file.read())
                    tmp_path = tmp.name
                wb = xlrd.open_workbook(tmp_path)
                sheet = wb.sheet_by_index(0)
                for row_idx in range(sheet.nrows):
                    cell = sheet.cell_value(row_idx, 0)
                    if cell:
                        iccid_list.append(str(cell).strip())
                os.remove(tmp_path)
            except Exception as e:
                messages.error(request, f'Excel文件读取失败: {e}')
                return redirect('/cards/import/')
        else:
            messages.error(request, '仅支持txt或Excel文件')
            return redirect('/cards/import/')
        
        # 统计变量
        success_count = 0  # 成功导入的卡数量
        existing_count = 0  # 已存在的卡数量
        failed_cards = []  # 接口查询失败或无数据的卡号
        not_found_cards = []  # 接口查询中没有找到的卡号
        
        # 获取API配置
        api_user = SystemConfig.objects.get(key='api_user').value
        api_pass = SystemConfig.objects.get(key='api_pass').value
        from time import sleep
        from django.db import IntegrityError
        
        if iccid_list:
            # 每100个一组查询接口
            for i in range(0, len(iccid_list), 100):
                # 每次接口调用前休眠1秒
                sleep(1)
                group = iccid_list[i:i+100]
                group_str = ','.join(group)
                
                try:
                    res = multiple_cards_query(userName=api_user, passWord=api_pass, iccid_list=group_str + ',')
                    if isinstance(res, str):
                        # 接口返回错误信息
                        failed_cards.extend(group)
                        print(f"接口返回异常: {res}")
                        continue
                    
                    if res and res.get('status') == 0:
                        card_data = res.get('data', {})
                        # 记录接口返回的iccid
                        returned_iccids = set()
                        
                        # 处理接口返回的卡信息
                        for card_info in card_data:
                            iccid = card_info.get('iccid')
                            if not iccid:
                                continue
                                
                            returned_iccids.add(iccid)
                            
                            # 检查数据库中是否已存在未软删除的卡
                            if Cards.objects.filter(iccid=iccid, delete_at__isnull=True).exists():
                                existing_count += 1
                                continue
                            
                            # 过滤允许的字段
                            allowed_fields = [f.name for f in Cards._meta.fields]
                            filtered_data = {k: v for k, v in card_info.items() if k in allowed_fields}
                            
                            try:
                                # 创建新卡记录
                                Cards.objects.create(**filtered_data)
                                success_count += 1
                            except IntegrityError as e:
                                # 数据库完整性错误
                                failed_cards.append(iccid)
                            except Exception as e:
                                # 其他异常
                                failed_cards.append(iccid)
                        
                        # 找出当前组中接口没有返回的卡号
                        current_group_set = set(group)
                        not_found_in_current_group = current_group_set - returned_iccids
                        not_found_cards.extend(list(not_found_in_current_group))
                        
                    else:
                        # 接口返回状态不为0
                        print(f"接口返回异常: {res}")
                        failed_cards.extend(group)
                        
                except Exception as e:
                    # 接口调用异常
                    print(f"接口返回异常: {res}")
                    failed_cards.extend(group)
        
        # 生成导入结果消息
        result_message = f'导入完成: 成功导入 {success_count} 张卡，已存在 {existing_count} 张卡'
        
        if not_found_cards:
            result_message += f'，接口查询无数据 {len(not_found_cards)} 张卡'
        
        if failed_cards:
            result_message += f'，接口查询失败 {len(failed_cards)} 张卡'
        
        # 详细信息
        if not_found_cards or failed_cards:
            detail_message = ""
            if not_found_cards:
                detail_message += f"接口查询无数据的卡号: {', '.join(not_found_cards[:10])}"
                if len(not_found_cards) > 10:
                    detail_message += f" 等{len(not_found_cards)}张卡"
            
            if failed_cards:
                if detail_message:
                    detail_message += "; "
                detail_message += f"接口查询失败的卡号: {', '.join(failed_cards[:10])}"
                if len(failed_cards) > 10:
                    detail_message += f" 等{len(failed_cards)}张卡"
            
            messages.warning(request, detail_message)
        
        messages.success(request, result_message)
        return redirect('/cards/')
    return render(request, 'cards/import.html')
def cards_view(request):
    """
    卡列表视图，展示当前用户可见的所有卡。
    :param request: HttpRequest对象
    :return: 渲染卡列表页面
    """
    from LogisticsCard.models import Customer
    customer_id = request.GET.get('customer')
    iccid = request.GET.get('iccid', '').strip()
    card_status = request.GET.get('cardStatus', '').strip()
    customers = Customer.objects.filter(delete_at__isnull=True)
    if request.user.is_superuser:
        cards = Cards.objects.filter(delete_at__isnull=True)
    else:
        card_ids = CardUserRelation.objects.filter(user=request.user).values_list('card_id', flat=True)
        cards = Cards.objects.filter(id__in=card_ids, delete_at__isnull=True)
    if customer_id:
        card_ids = CardUserRelation.objects.filter(customer_id=customer_id).values_list('card_id', flat=True)
        cards = cards.filter(id__in=card_ids)
    if iccid:
        cards = cards.filter(iccid__icontains=iccid)
    if card_status:
        cards = cards.filter(cardStatus=card_status)
    cards = cards.order_by('-id')
    paginator = Paginator(cards, 20)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    my_assigned_card_ids = set(CardUserRelation.objects.filter(operator=request.user).values_list('card_id', flat=True))
    all_assigned_card_ids = set(CardUserRelation.objects.values_list('card_id', flat=True))
    return render(request, 'cards/index.html', {
        'page_obj': page_obj,
        'operator_cn': operator_cn,
        'card_status_cn': card_status_cn,
        'card_type_cn': card_type_cn,
        'real_name_auth_cn': real_name_auth_cn,
        'my_assigned_card_ids': my_assigned_card_ids,
        'all_assigned_card_ids': all_assigned_card_ids,
        'customers': customers,
        'selected_customer': customer_id,
        'selected_iccid': iccid,
        'selected_card_status': card_status
    })

@login_required
def add_card_view(request):
    if request.method == 'POST':
        if Cards.objects.filter(iccid=request.POST.get('iccid'), delete_at__isnull=True).exists():
            return render(request, 'cards/add.html', {'error': 'ICCID已存在'})
        if Cards.objects.filter(msisdn=request.POST.get('msisdn'), delete_at__isnull=True).exists():
            return render(request, 'cards/add.html', {'error': 'MSISDN已存在'})
        res = single_card_detail_query(
            userName=SystemConfig.objects.get(key='api_user').value,
            passWord=SystemConfig.objects.get(key='api_pass').value,
            iccid=request.POST.get('iccid'),
            msisdn=request.POST.get('msisdn')
        )
        if isinstance(res, str):
            return render(request, 'cards/add.html', {'error': res})
        data = res.get('data')
        allowed_fields = [f.name for f in Cards._meta.fields]
        filtered_data = {k: v for k, v in data.items() if k in allowed_fields}
        card_obj = Cards.objects.create(**filtered_data)
        user_id = request.POST.get('user_id')
        if user_id:
            try:
                user_obj = User.objects.get(id=user_id)
                CardUserRelation.objects.create(user=user_obj, operator=request.user, card=card_obj)
            except User.DoesNotExist:
                pass
        Log.objects.create(user=request.user, action='other', detail=f'添加卡 ICCID:{card_obj.iccid}')
        return redirect('/cards/')
    users = User.objects.filter(is_superuser=False, profile__delete_at__isnull=True, id__in=UserProfile.objects.filter(operator=request.user).values_list('user_id', flat=True))
    return render(request, 'cards/add.html', {'users': users})

@login_required
def edit_card_view(request, card_id):
    card = Cards.objects.get(id=card_id, delete_at__isnull=True)
    if request.method == 'POST':
        card.iccid = request.POST.get('iccid')
        card.msisdn = request.POST.get('msisdn')
        card.save()
        Log.objects.create(user=request.user, action='other', detail=f'编辑卡 ICCID:{card.iccid}')
        return redirect('/cards/')
    return render(request, 'cards/edit.html', {'card': card})

@login_required
def assign_card_view(request, card_id):
    card = get_object_or_404(Cards, id=card_id, delete_at__isnull=True)
    # 只允许分配未分配的卡
    if CardUserRelation.objects.filter(card=card).exists():
        return redirect('/cards/')
    if request.method == 'POST':
        user_id = request.POST.get('user_id')
        if user_id:
            try:
                user_obj = User.objects.get(id=user_id)
                CardUserRelation.objects.create(user=user_obj, operator=request.user, card=card)
                Log.objects.create(user=request.user, action='other', detail=f'分配卡 ICCID:{card.iccid} 给用户:{user_obj.username}')
                return redirect('/cards/')
            except User.DoesNotExist:
                pass
    users = User.objects.filter(is_superuser=False)
    return render(request, 'cards/assign.html', {'card': card, 'users': users})

@login_required
def delete_card_view(request, card_id):
    from django.utils import timezone
    card = get_object_or_404(Cards, id=card_id, delete_at__isnull=True)
    iccid = getattr(card, "iccid", card_id)
    card.delete_at = timezone.now()
    card.save()
    Log.objects.create(user=request.user, action='other', detail=f'虚拟删除卡 ICCID:{iccid}')
    return redirect('/cards/')
@login_required
def export_cards_view(request):
    """
    卡导出视图，将当前可见卡列表导出为Excel文件。
    """
    import openpyxl
    from openpyxl import Workbook
    wb = Workbook()
    ws = wb.active
    ws.title = "卡列表"
    # 表头
    headers = ["ICCID", "MSISDN", "是否实名", "IMSI", "IMEI", "卡类型", "卡状态", "运营商", "套餐名称", "套餐总流量", "卡剩余流量", "激活时间", "套餐到期"]
    if request.user.is_superuser or hasattr(request.user, 'profile') and getattr(request.user.profile, 'type', None) == 'admin':
        headers.insert(2, "绑定客户")
    ws.append(headers)
    # 获取卡数据
    if request.user.is_superuser:
        cards = Cards.objects.filter(delete_at__isnull=True).order_by('-id')
    else:
        card_ids = CardUserRelation.objects.filter(user=request.user).values_list('card_id', flat=True)
        cards = Cards.objects.filter(id__in=card_ids, delete_at__isnull=True).order_by('-id')
    for card in cards:
        row = [card.iccid, card.msisdn]
        if request.user.is_superuser or hasattr(request.user, 'profile') and getattr(request.user.profile, 'type', None) == 'admin':
            rels = card.carduserrelation_set.all()
            if rels:
                customer_names = ','.join([getattr(rel.customer, 'username', '') for rel in rels])
            else:
                customer_names = '无'
            row.append(customer_names)
        row += [
            real_name_auth_cn.get(card.realNameStatus, card.realNameStatus),
            card.imsi,
            card.imei,
            card_type_cn.get(card.cardType, card.cardType),
            card_status_cn.get(card.cardStatus, card.cardStatus),
            operator_cn.get(card.operator, card.operator),
            card.packageName,
            card.totalFlow,
            card.cardFlow,
            card.activateTime,
            card.packageTime
        ]
        ws.append(row)
    import tempfile
    with tempfile.NamedTemporaryFile(delete=False, suffix='.xlsx') as tmp:
        wb.save(tmp.name)
        tmp.seek(0)
        response = HttpResponse(tmp.read(), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        response['Content-Disposition'] = 'attachment; filename="cards_export.xlsx"'
    return response

